Data Overview

readr::read_csv(here("data/character_list5.csv"),
                      progress = FALSE,
                      col_types = cols(
                                    script_id = col_integer(),
                                    imdb_character_name = col_character(),
                                    words = col_integer(),
                                    gender = col_character(),
                                    age = col_character()
                                    )) %>%
  mutate(age = as.numeric(age)) -> characters_list
characters_list %>% 
  glimpse()
Observations: 23,048
Variables: 5
$ script_id           <int> 280, 280, 280, 280, 280, 280, 280, 623, 623, 623, 623, 623, 623, 623...
$ imdb_character_name <chr> "betty", "carolyn johnson", "eleanor", "francesca johns", "madge", "...
$ words               <int> 311, 873, 138, 2251, 190, 723, 1908, 328, 409, 347, 2020, 366, 160, ...
$ gender              <chr> "f", "f", "f", "f", "f", "m", "m", "m", "f", "m", "m", "m", "m", "m"...
$ age                 <dbl> 35, NA, NA, 46, 46, 38, 65, NA, 28, NA, 58, 53, 25, 39, 33, NA, 34, ...
readr::read_csv(here("data/meta_data7.csv"),
                      progress = FALSE,
         col_types = cols(
                        script_id = col_integer(),
                        imdb_id = col_character(),
                        title = col_character(),
                        year = col_integer(),
                        gross = col_integer(),
                        lines_data = col_character()
                        )) %>%
  mutate(title = iconv(title,"latin1", "UTF-8")) -> meta_data
meta_data %>%
  glimpse()
Observations: 2,000
Variables: 6
$ script_id  <int> 1534, 1512, 1514, 1517, 1520, 6537, 3778, 623, 1525, 6030, 625, 1509, 8543, 7...
$ imdb_id    <chr> "tt1022603", "tt0147800", "tt0417385", "tt2024544", "tt1542344", "tt0450385",...
$ title      <chr> "(500) Days of Summer", "10 Things I Hate About You", "12 and Holding", "12 Y...
$ year       <int> 2009, 1999, 2005, 2013, 2010, 2007, 1992, 2001, 2009, 2013, 1968, 2009, 2008,...
$ gross      <int> 37, 65, NA, 60, 20, 91, 15, 37, 74, 80, 376, 192, 98, 204, 19, 59, 67, 36, 32...
$ lines_data <chr> "7435445256774774443342577775657744434444564456745433675534527777342375445534...

Combinando Dados Originais

scripts_data %>%
  glimpse()
Observations: 18,968
Variables: 16
$ script_id           <int> 280, 280, 280, 280, 280, 280, 280, 623, 623, 623, 623, 623, 623, 623...
$ imdb_character_name <chr> "betty", "carolyn johnson", "eleanor", "francesca johns", "madge", "...
$ words               <int> 311, 873, 138, 2251, 190, 723, 1908, 328, 409, 347, 2020, 366, 160, ...
$ gender              <chr> "f", "f", "f", "f", "f", "m", "m", "m", "f", "m", "m", "m", "m", "m"...
$ age                 <dbl> 35, NA, NA, 46, 46, 38, 65, NA, 28, NA, 58, 53, 25, 39, 33, NA, 34, ...
$ imdb_id             <chr> "tt0112579", "tt0112579", "tt0112579", "tt0112579", "tt0112579", "tt...
$ title               <chr> "The Bridges of Madison County", "The Bridges of Madison County", "T...
$ year                <int> 1995, 1995, 1995, 1995, 1995, 1995, 1995, 2001, 2001, 2001, 2001, 20...
$ gross               <int> 142, 142, 142, 142, 142, 142, 142, 37, 37, 37, 37, 37, 37, 37, 37, 3...
$ lines_data          <chr> "4332023434343443203433434334433434343434434344344333434443444344233...
$ fem_words           <dbl> 311, 873, 138, 2251, 190, 0, 0, 0, 409, 0, 0, 0, 0, 0, 0, 148, 801, ...
$ man_words           <dbl> 0, 0, 0, 0, 0, 723, 1908, 328, 0, 347, 2020, 366, 160, 1337, 1683, 0...
$ total_fem_words     <dbl> 3763, 3763, 3763, 3763, 3763, 3763, 3763, 1524, 1524, 1524, 1524, 15...
$ total_man_words     <dbl> 2631, 2631, 2631, 2631, 2631, 2631, 2631, 7584, 7584, 7584, 7584, 75...
$ f_m_ratio           <dbl> 2.5000000, 2.5000000, 2.5000000, 2.5000000, 2.5000000, 2.5000000, 2....
$ f_m_wordratio       <dbl> 1.43025466, 1.43025466, 1.43025466, 1.43025466, 1.43025466, 1.430254...

Proporção entre dialógo feminino e masculino

scripts_data %>%
  group_by(title,year) %>%
  slice(1) %>%
  unique() %>%
  ggplot(aes(x=f_m_wordratio,
             y=(..count..)/sum(..count..))) +
  geom_histogram(binwidth = 0.1,
                 boundary = 0,
                 fill = "grey",
                 color = "black")

  • Em alguns raríssimos exemplos há muito mais dialógo feminino que feminino.
scripts_data %>%
  group_by(title,year) %>%
  slice(1) %>%
  unique() %>%
  filter(f_m_wordratio < 10) %>%
  ggplot(aes(x=f_m_wordratio,
             y=(..count..)/sum(..count..))) +
  geom_histogram(binwidth = 0.1,
                 fill = "grey",
                 color = "black")

  • Uma vez que filtramos os casos mais raros é possível ver que há uma forte domínio do dialógo masculino sobre o feminino nos filmes.
scripts_data %>%
  group_by(title,year) %>%
  slice(1) %>%
  unique() %>%
  ggplot(aes(x="", 
             y=f_m_wordratio)) +
  geom_violin(fill="grey",
               width=0.5)

  • É ainda mais óbvio:
    • A presença de alguns poucos casos de completo domínio do diálogo feminino
    • O geral domínio do dialógo masculino sobre feminino

Proporção entre personagens femininos e masculinos

scripts_data %>%
  group_by(title,year) %>%
  unique() %>%
  ggplot(aes(x=f_m_ratio,
             y=(..count..)/sum(..count..))) +
  geom_histogram(binwidth = 0.1,
                 boundary = 0,
                 fill = "grey",
                 color = "black") +
  scale_x_continuous(breaks = seq(0,10,0.5))

  • É nítido o domínio de personagens masculinos
scripts_data %>%
  group_by(title,year) %>%
  unique() %>%
  ggplot(aes(x="", 
             y=f_m_ratio)) +
  geom_violin(fill="grey",
               width=0.5)

  • Além do forte domínio de personangens masculinos é possível ver a existência de algumas instâncias, embora raras de uma avassaladora presença femininina, (e.g 10 vezes mais mulheres que homens).

Ano do filme

scripts_data %>%
  group_by(title,year) %>%
  unique() %>%
  ggplot(aes(x=year)) +
  geom_bar(fill = "grey",
           color = "black")

  • Os filmes são sua maioria recentes, a quase totalidade dos filmes foi lançada a partir dos anos 1990.
scripts_data %>%
  group_by(title,year) %>%
  unique() %>%
  ggplot(aes(x="", 
             y=year)) +
  geom_violin(fill="grey",
               width=0.5)

  • Ainda é possível ver uma presença significativa de filmes do começo dos anos 1980.
  • Existem alguns filmes anteriores aos próprio anos 1950.

Faturamento do filme

scripts_data %>%
  group_by(title,year) %>%
  unique() %>%
  ggplot(aes(x=gross,
             y=(..count..)/sum(..count..))) +
  geom_histogram(binwidth = 50,
                 boundary = 0,
                 fill = "grey",
                 color = "black")

  • Faturamento baixo ou razoável para a maior parte dos filmes.
  • Alguns poucos filmes tiveram um faturamento esmagador.
scripts_data %>%
  group_by(title,year) %>%
  unique() %>%
  ggplot(aes(x="", 
             y=gross)) +
  geom_violin(fill="grey",
               width=0.5)

  • Resultados similares aos do respectivo histograma.

Aplicando escala apropriada aos dados.

scripts_data %>%
  group_by(title) %>%
  slice(1) %>%
  unique() %>%
  ungroup() %>%
  select(title,
         gross,
         f_m_ratio,
         f_m_wordratio) -> data
select(data, -title) %>%
mutate_all(funs(scale)) -> scaled_data
scaled_data %>% 
  sample_n(10)

Número K ótimo

Estatística GAP

A estatística GAP compara a solução do agrupamento com cada k com a solução em um dataset onde não há estrutura de grupos.

plot_clusgap = function(clusgap, title="Gap Statistic calculation results"){
    require("ggplot2")
    gstab = data.frame(clusgap$Tab, k=1:nrow(clusgap$Tab))
    p = ggplot(gstab, aes(k, gap)) + geom_line() + geom_point(size=5)
    p = p + geom_errorbar(aes(ymax=gap+SE.sim, ymin=gap-SE.sim), width = .2)
    p = p + ggtitle(title)
    return(p)
}
gaps <- scaled_data %>% 
    clusGap(FUN = kmeans,
            nstart = 20,
            K.max = 8,
            B = 200,
            iter.max=30)
Clustering k = 1,2,..., K.max (= 8): .. done
Bootstrapping, b = 1,2,..., B (= 200)  [one "." per sample]:
.................................................. 50 
.................................................. 100 
.................................................. 150 
.................................................. 200 
plot_clusgap(gaps)

  • 3 ou 6 grupos parece apropiado, mas como 6 é precedido por uma série de quedas 3 seria uma melhor opção.

Elbow Method

set.seed(123)
# Compute and plot wss for k = 2 to k = 15.
k.max <- 15
wss <- sapply(1:k.max, 
              function(k){kmeans(scaled_data, k, nstart=50,iter.max = 15 )$tot.withinss})
plot(1:k.max, wss,
     type="b", pch = 19, frame = FALSE, 
     xlab="Number of clusters K",
     ylab="Total within-clusters sum of squares")

  • Pelo Elbow method 3 parece ser um bom número de grupos devido à queda de 3 para 4.

Bayesian Information Criterion

  • Visualmente K= 2 e K = 3 representam o ganho mais significativo em termos de BIC (Bayesian Information Criterion)

Hubert Index e D Index

nb <- NbClust(scaled_data, diss=NULL, distance = "euclidean", 
              min.nc=2, max.nc=5, method = "kmeans", 
              index = "all", alphaBeale = 0.1)
*** : The Hubert index is a graphical method of determining the number of clusters.
                In the plot of Hubert index, we seek a significant knee that corresponds to a 
                significant increase of the value of the measure i.e the significant peak in Hubert
                index second differences plot. 
 

*** : The D index is a graphical method of determining the number of clusters. 
                In the plot of D index, we seek a significant knee (the significant peak in Dindex
                second differences plot) that corresponds to a significant increase of the value of
                the measure. 
 
******************************************************************* 
* Among all indices:                                                
* 5 proposed 2 as the best number of clusters 
* 8 proposed 3 as the best number of clusters 
* 2 proposed 4 as the best number of clusters 
* 8 proposed 5 as the best number of clusters 

                   ***** Conclusion *****                            
 
* According to the majority rule, the best number of clusters is  3 
 
 
******************************************************************* 

hist(nb$Best.nc[1,], breaks = max(na.omit(nb$Best.nc[1,])))

  • O índice de Hubert e o índice D sugerem K = 3 como a melhor solução

K-Means


Clustering

n_clusters = 3
scaled_data %>%
    kmeans(n_clusters, iter.max = 100, nstart = 20) -> km
p <- autoplot(km, data=scaled_data, frame = TRUE)  
ggplotly(p)
  • É possível ver os 3 grupos nitidamente distintos, por meio de um zoom percebe-se que embora o grupo 1 e o grupo 3 estejam próximos o overlap é basicamente inexistente.
row.names(scaled_data) <- data$title
toclust <- scaled_data %>% 
    rownames_to_column(var = "title") 
km = toclust %>% 
    select(-title) %>% 
    kmeans(centers = n_clusters, iter.max = 100, nstart = 20)
km %>% 
    augment(toclust) %>% 
    gather(key = "variável", value = "valor", -title, -.cluster) %>% 
    ggplot(aes(x = `variável`, y = valor, group = title, colour = .cluster)) + 
    geom_point(alpha = 0.2) + 
    geom_line(alpha = .5) + 
    facet_wrap(~ .cluster) +
    coord_flip()


  • Grupo 1 - We Can Do It!
    • Menor Faturamento
    • Mais dialógo para as mulheres
    • Maior taxa de personagens femininos


  • Grupo 2 - It’s A Man’s Man’s Man’s World
    • Maior faturamento entre todos
    • Menor taxa de dialógo para as mulheres
    • Menor taxa de personagens femininos


  • Grupo 3 - Sitting on the Fence
    • Mediano em termos de dialógo, personagens e faturamento

Silhouette

dists = scaled_data %>% 
  dist()
scaled_data %>%
    kmeans(3, iter.max = 100, nstart = 20) -> km
silhouette(km$cluster, dists) %>%
   plot(col = RColorBrewer::brewer.pal(4, "Set2"),border=NA)

  • O valor de 0.6 da silhueta significa que a nossa clusterização foi razoável. ヾ(⌐■_■)ノ♪
LS0tCnRpdGxlOiAiRGlzdHJpYnVpw6fDo28gZGUgZGlhbMOzZ28gZW0gZmlsbWVzIgphdXRob3I6ICJKb3PDqSBCZW5hcmRpIGRlIFNvdXphIE51bmVzIgpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazoKICAgIHRvYzogeWVzCiAgICB0b2NfZmxvYXQ6IHllcwogIGh0bWxfZG9jdW1lbnQ6CiAgICBkZl9wcmludDogcGFnZWQKICAgIHRvYzogeWVzCiAgICB0b2NfZmxvYXQ6IHllcwotLS0KCgpgYGB7ciBzZXR1cCwgZWNobz1GQUxTRSwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0KCmxpYnJhcnkoaGVyZSkKbGlicmFyeShicm9vbSkKbGlicmFyeSh2ZWdhbikKbGlicmFyeShtY2x1c3QpCmxpYnJhcnkocGxvdGx5KQpsaWJyYXJ5KE5iQ2x1c3QpCmxpYnJhcnkobGF0dGljZSkKbGlicmFyeShjbHVzdGVyKQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShnZ2ZvcnRpZnkpCgp0aGVtZV9zZXQodGhlbWVfYncoKSkKYGBgCgojIERhdGEgT3ZlcnZpZXcKCmBgYHtyLCB3YXJuaW5nPUZBTFNFfQpyZWFkcjo6cmVhZF9jc3YoaGVyZSgiZGF0YS9jaGFyYWN0ZXJfbGlzdDUuY3N2IiksCiAgICAgICAgICAgICAgICAgICAgICBwcm9ncmVzcyA9IEZBTFNFLAogICAgICAgICAgICAgICAgICAgICAgY29sX3R5cGVzID0gY29scygKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2NyaXB0X2lkID0gY29sX2ludGVnZXIoKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaW1kYl9jaGFyYWN0ZXJfbmFtZSA9IGNvbF9jaGFyYWN0ZXIoKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgd29yZHMgPSBjb2xfaW50ZWdlcigpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBnZW5kZXIgPSBjb2xfY2hhcmFjdGVyKCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFnZSA9IGNvbF9jaGFyYWN0ZXIoKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApKSAlPiUKICBtdXRhdGUoYWdlID0gYXMubnVtZXJpYyhhZ2UpKSAtPiBjaGFyYWN0ZXJzX2xpc3QKCmNoYXJhY3RlcnNfbGlzdCAlPiUgCiAgZ2xpbXBzZSgpCgpgYGAKCmBgYHtyfQpyZWFkcjo6cmVhZF9jc3YoaGVyZSgiZGF0YS9tZXRhX2RhdGE3LmNzdiIpLAogICAgICAgICAgICAgICAgICAgICAgcHJvZ3Jlc3MgPSBGQUxTRSwKICAgICAgICAgY29sX3R5cGVzID0gY29scygKICAgICAgICAgICAgICAgICAgICAgICAgc2NyaXB0X2lkID0gY29sX2ludGVnZXIoKSwKICAgICAgICAgICAgICAgICAgICAgICAgaW1kYl9pZCA9IGNvbF9jaGFyYWN0ZXIoKSwKICAgICAgICAgICAgICAgICAgICAgICAgdGl0bGUgPSBjb2xfY2hhcmFjdGVyKCksCiAgICAgICAgICAgICAgICAgICAgICAgIHllYXIgPSBjb2xfaW50ZWdlcigpLAogICAgICAgICAgICAgICAgICAgICAgICBncm9zcyA9IGNvbF9pbnRlZ2VyKCksCiAgICAgICAgICAgICAgICAgICAgICAgIGxpbmVzX2RhdGEgPSBjb2xfY2hhcmFjdGVyKCkKICAgICAgICAgICAgICAgICAgICAgICAgKSkgJT4lCiAgbXV0YXRlKHRpdGxlID0gaWNvbnYodGl0bGUsImxhdGluMSIsICJVVEYtOCIpKSAtPiBtZXRhX2RhdGEKCm1ldGFfZGF0YSAlPiUKICBnbGltcHNlKCkKCmBgYAoKIyMjIyBDb21iaW5hbmRvIERhZG9zIE9yaWdpbmFpcwoKYGBge3J9CmxlZnRfam9pbihjaGFyYWN0ZXJzX2xpc3QsIAogICAgICAgICAgbWV0YV9kYXRhLCAKICAgICAgICAgIGJ5PWMoInNjcmlwdF9pZCIpKSAlPiUKICBncm91cF9ieSh0aXRsZSwgeWVhcikgJT4lCiAgZHJvcF9uYShncm9zcykgJT4lCiAgdW5ncm91cCgpIC0+IHNjcmlwdHNfZGF0YQoKc2NyaXB0c19kYXRhICU+JQogIGdsaW1wc2UoKQpgYGAKCmBgYHtyfQpzY3JpcHRzX2RhdGEgJT4lCiAgbXV0YXRlKGZlbV93b3JkcyA9IGlmZWxzZShnZW5kZXIgPT0gImYiLHdvcmRzLDApLAogICAgICAgICBtYW5fd29yZHMgPSBpZmVsc2UoZ2VuZGVyID09ICJtIix3b3JkcywwKSkgJT4lCiAgZ3JvdXBfYnkodGl0bGUsIHllYXIpICU+JQogIG11dGF0ZSh0b3RhbF9mZW1fd29yZHMgPSBzdW0oZmVtX3dvcmRzKSwKICAgICAgICAgdG90YWxfbWFuX3dvcmRzID0gc3VtKG1hbl93b3JkcykpICU+JQogIGZpbHRlcih0b3RhbF9mZW1fd29yZHMgIT0gIDApICU+JQogIGZpbHRlcih0b3RhbF9tYW5fd29yZHMgIT0gIDApICU+JQogICAgbXV0YXRlKGZfbV9yYXRpbyA9IHN1bShnZW5kZXIgPT0gImYiKS9zdW0oZ2VuZGVyID09ICJtIiksCiAgICAgICAgICAgZl9tX3dvcmRyYXRpbyA9IHRvdGFsX2ZlbV93b3Jkcy90b3RhbF9tYW5fd29yZHMpICU+JQogIHVuZ3JvdXAoKSAgLT4gc2NyaXB0c19kYXRhCgpzY3JpcHRzX2RhdGEgJT4lCiAgc2VsZWN0KHRpdGxlLAogICAgICAgICB5ZWFyLAogICAgICAgICBmX21fcmF0aW8sCiAgICAgICAgIGZfbV93b3JkcmF0aW8pICU+JQogIHNhbXBsZV9uKDEwKQpgYGAKCiMjIFByb3BvcsOnw6NvIGVudHJlIGRpYWzDs2dvIGZlbWluaW5vIGUgbWFzY3VsaW5vCgpgYGB7cn0Kc2NyaXB0c19kYXRhICU+JQogIGdyb3VwX2J5KHRpdGxlLHllYXIpICU+JQogIHNsaWNlKDEpICU+JQogIHVuaXF1ZSgpICU+JQogIGdncGxvdChhZXMoeD1mX21fd29yZHJhdGlvLAogICAgICAgICAgICAgeT0oLi5jb3VudC4uKS9zdW0oLi5jb3VudC4uKSkpICsKICBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aCA9IDAuMSwKICAgICAgICAgICAgICAgICBib3VuZGFyeSA9IDAsCiAgICAgICAgICAgICAgICAgZmlsbCA9ICJncmV5IiwKICAgICAgICAgICAgICAgICBjb2xvciA9ICJibGFjayIpCmBgYAoKKiBFbSBhbGd1bnMgcmFyw61zc2ltb3MgZXhlbXBsb3MgaMOhIG11aXRvIG1haXMgZGlhbMOzZ28gZmVtaW5pbm8gcXVlIGZlbWluaW5vLiAKCmBgYHtyfQpzY3JpcHRzX2RhdGEgJT4lCiAgZ3JvdXBfYnkodGl0bGUseWVhcikgJT4lCiAgc2xpY2UoMSkgJT4lCiAgdW5pcXVlKCkgJT4lCiAgZmlsdGVyKGZfbV93b3JkcmF0aW8gPCAxMCkgJT4lCiAgZ2dwbG90KGFlcyh4PWZfbV93b3JkcmF0aW8sCiAgICAgICAgICAgICB5PSguLmNvdW50Li4pL3N1bSguLmNvdW50Li4pKSkgKwogIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gMC4xLAogICAgICAgICAgICAgICAgIGZpbGwgPSAiZ3JleSIsCiAgICAgICAgICAgICAgICAgY29sb3IgPSAiYmxhY2siKQpgYGAKCiogVW1hIHZleiBxdWUgZmlsdHJhbW9zIG9zIGNhc29zIG1haXMgcmFyb3Mgw6kgcG9zc8OtdmVsIHZlciBxdWUgaMOhIHVtYSBmb3J0ZSBkb23DrW5pbyBkbyBkaWFsw7NnbyBtYXNjdWxpbm8gc29icmUgbyBmZW1pbmlubyBub3MgZmlsbWVzLgoKYGBge3J9CnNjcmlwdHNfZGF0YSAlPiUKICBncm91cF9ieSh0aXRsZSx5ZWFyKSAlPiUKICBzbGljZSgxKSAlPiUKICB1bmlxdWUoKSAlPiUKICBnZ3Bsb3QoYWVzKHg9IiIsIAogICAgICAgICAgICAgeT1mX21fd29yZHJhdGlvKSkgKwogIGdlb21fdmlvbGluKGZpbGw9ImdyZXkiLAogICAgICAgICAgICAgICB3aWR0aD0wLjUpCmBgYAoKKiDDiSBhaW5kYSBtYWlzIMOzYnZpbzoKICAgICogQSBwcmVzZW7Dp2EgZGUgYWxndW5zIHBvdWNvcyBjYXNvcyBkZSBjb21wbGV0byBkb23DrW5pbyBkbyBkacOhbG9nbyBmZW1pbmlubwogICAgKiBPIGdlcmFsIGRvbcOtbmlvIGRvIGRpYWzDs2dvIG1hc2N1bGlubyBzb2JyZSBmZW1pbmlubwoKIyMgUHJvcG9yw6fDo28gZW50cmUgcGVyc29uYWdlbnMgZmVtaW5pbm9zIGUgbWFzY3VsaW5vcyAKCmBgYHtyfQpzY3JpcHRzX2RhdGEgJT4lCiAgZ3JvdXBfYnkodGl0bGUseWVhcikgJT4lCiAgdW5pcXVlKCkgJT4lCiAgZ2dwbG90KGFlcyh4PWZfbV9yYXRpbywKICAgICAgICAgICAgIHk9KC4uY291bnQuLikvc3VtKC4uY291bnQuLikpKSArCiAgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSAwLjEsCiAgICAgICAgICAgICAgICAgYm91bmRhcnkgPSAwLAogICAgICAgICAgICAgICAgIGZpbGwgPSAiZ3JleSIsCiAgICAgICAgICAgICAgICAgY29sb3IgPSAiYmxhY2siKSArCiAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IHNlcSgwLDEwLDAuNSkpCmBgYAoKKiDDiSBuw610aWRvIG8gZG9tw61uaW8gZGUgcGVyc29uYWdlbnMgbWFzY3VsaW5vcwoKYGBge3J9CnNjcmlwdHNfZGF0YSAlPiUKICBncm91cF9ieSh0aXRsZSx5ZWFyKSAlPiUKICB1bmlxdWUoKSAlPiUKICBnZ3Bsb3QoYWVzKHg9IiIsIAogICAgICAgICAgICAgeT1mX21fcmF0aW8pKSArCiAgZ2VvbV92aW9saW4oZmlsbD0iZ3JleSIsCiAgICAgICAgICAgICAgIHdpZHRoPTAuNSkKYGBgCgoqIEFsw6ltIGRvIGZvcnRlIGRvbcOtbmlvIGRlIHBlcnNvbmFuZ2VucyBtYXNjdWxpbm9zIMOpIHBvc3PDrXZlbCB2ZXIgYSBleGlzdMOqbmNpYSBkZSBhbGd1bWFzIGluc3TDom5jaWFzLCBlbWJvcmEgcmFyYXMgZGUgdW1hIGF2YXNzYWxhZG9yYSBwcmVzZW7Dp2EgZmVtaW5pbmluYSwgKGUuZyAxMCB2ZXplcyBtYWlzIG11bGhlcmVzIHF1ZSBob21lbnMpLgoKIyMgQW5vIGRvIGZpbG1lIAoKYGBge3J9CnNjcmlwdHNfZGF0YSAlPiUKICBncm91cF9ieSh0aXRsZSx5ZWFyKSAlPiUKICB1bmlxdWUoKSAlPiUKICBnZ3Bsb3QoYWVzKHg9eWVhcikpICsKICBnZW9tX2JhcihmaWxsID0gImdyZXkiLAogICAgICAgICAgIGNvbG9yID0gImJsYWNrIikKYGBgCgoqIE9zIGZpbG1lcyBzw6NvIHN1YSBtYWlvcmlhIHJlY2VudGVzLCBhIHF1YXNlIHRvdGFsaWRhZGUgZG9zIGZpbG1lcyBmb2kgbGFuw6dhZGEgYSBwYXJ0aXIgZG9zIGFub3MgMTk5MC4KCmBgYHtyfQpzY3JpcHRzX2RhdGEgJT4lCiAgZ3JvdXBfYnkodGl0bGUseWVhcikgJT4lCiAgdW5pcXVlKCkgJT4lCiAgZ2dwbG90KGFlcyh4PSIiLCAKICAgICAgICAgICAgIHk9eWVhcikpICsKICBnZW9tX3Zpb2xpbihmaWxsPSJncmV5IiwKICAgICAgICAgICAgICAgd2lkdGg9MC41KQpgYGAKCiogQWluZGEgw6kgcG9zc8OtdmVsIHZlciB1bWEgcHJlc2Vuw6dhIHNpZ25pZmljYXRpdmEgZGUgZmlsbWVzIGRvIGNvbWXDp28gZG9zIGFub3MgMTk4MC4KKiBFeGlzdGVtIGFsZ3VucyBmaWxtZXMgYW50ZXJpb3JlcyBhb3MgcHLDs3ByaW8gYW5vcyAxOTUwLgoKIyMgRmF0dXJhbWVudG8gZG8gZmlsbWUgCgpgYGB7cn0Kc2NyaXB0c19kYXRhICU+JQogIGdyb3VwX2J5KHRpdGxlLHllYXIpICU+JQogIHVuaXF1ZSgpICU+JQogIGdncGxvdChhZXMoeD1ncm9zcywKICAgICAgICAgICAgIHk9KC4uY291bnQuLikvc3VtKC4uY291bnQuLikpKSArCiAgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSA1MCwKICAgICAgICAgICAgICAgICBib3VuZGFyeSA9IDAsCiAgICAgICAgICAgICAgICAgZmlsbCA9ICJncmV5IiwKICAgICAgICAgICAgICAgICBjb2xvciA9ICJibGFjayIpCmBgYAoKKiBGYXR1cmFtZW50byBiYWl4byBvdSByYXpvw6F2ZWwgcGFyYSBhIG1haW9yIHBhcnRlIGRvcyBmaWxtZXMuCiogQWxndW5zIHBvdWNvcyBmaWxtZXMgdGl2ZXJhbSB1bSBmYXR1cmFtZW50byBlc21hZ2Fkb3IuCgpgYGB7cn0Kc2NyaXB0c19kYXRhICU+JQogIGdyb3VwX2J5KHRpdGxlLHllYXIpICU+JQogIHVuaXF1ZSgpICU+JQogIGdncGxvdChhZXMoeD0iIiwgCiAgICAgICAgICAgICB5PWdyb3NzKSkgKwogIGdlb21fdmlvbGluKGZpbGw9ImdyZXkiLAogICAgICAgICAgICAgICB3aWR0aD0wLjUpCmBgYAoKKiBSZXN1bHRhZG9zIHNpbWlsYXJlcyBhb3MgZG8gcmVzcGVjdGl2byBoaXN0b2dyYW1hLgoKIyMgQXBsaWNhbmRvIGVzY2FsYSBhcHJvcHJpYWRhIGFvcyBkYWRvcy4KCmBgYHtyfQpzY3JpcHRzX2RhdGEgJT4lCiAgZ3JvdXBfYnkodGl0bGUpICU+JQogIHNsaWNlKDEpICU+JQogIHVuaXF1ZSgpICU+JQogIHVuZ3JvdXAoKSAlPiUKICBzZWxlY3QodGl0bGUsCiAgICAgICAgIGdyb3NzLAogICAgICAgICBmX21fcmF0aW8sCiAgICAgICAgIGZfbV93b3JkcmF0aW8pIC0+IGRhdGEKCnNlbGVjdChkYXRhLCAtdGl0bGUpICU+JQptdXRhdGVfYWxsKGZ1bnMoc2NhbGUpKSAtPiBzY2FsZWRfZGF0YQoKc2NhbGVkX2RhdGEgJT4lIAogIHNhbXBsZV9uKDEwKQpgYGAKCiMgIE7Dum1lcm8gSyDDs3RpbW8gCgojIyBFc3RhdMOtc3RpY2EgR0FQIAoKQSBlc3RhdMOtc3RpY2EgR0FQIGNvbXBhcmEgYSBzb2x1w6fDo28gZG8gYWdydXBhbWVudG8gY29tIGNhZGEgayBjb20gYSBzb2x1w6fDo28gZW0gdW0gZGF0YXNldCBvbmRlIG7Do28gaMOhIGVzdHJ1dHVyYSBkZSBncnVwb3MuIAoKYGBge3J9CnBsb3RfY2x1c2dhcCA9IGZ1bmN0aW9uKGNsdXNnYXAsIHRpdGxlPSJHYXAgU3RhdGlzdGljIGNhbGN1bGF0aW9uIHJlc3VsdHMiKXsKICAgIHJlcXVpcmUoImdncGxvdDIiKQogICAgZ3N0YWIgPSBkYXRhLmZyYW1lKGNsdXNnYXAkVGFiLCBrPTE6bnJvdyhjbHVzZ2FwJFRhYikpCiAgICBwID0gZ2dwbG90KGdzdGFiLCBhZXMoaywgZ2FwKSkgKyBnZW9tX2xpbmUoKSArIGdlb21fcG9pbnQoc2l6ZT01KQogICAgcCA9IHAgKyBnZW9tX2Vycm9yYmFyKGFlcyh5bWF4PWdhcCtTRS5zaW0sIHltaW49Z2FwLVNFLnNpbSksIHdpZHRoID0gLjIpCiAgICBwID0gcCArIGdndGl0bGUodGl0bGUpCiAgICByZXR1cm4ocCkKfQpgYGAKCmBgYHtyfQpnYXBzIDwtIHNjYWxlZF9kYXRhICU+JSAKICAgIGNsdXNHYXAoRlVOID0ga21lYW5zLAogICAgICAgICAgICBuc3RhcnQgPSAyMCwKICAgICAgICAgICAgSy5tYXggPSA4LAogICAgICAgICAgICBCID0gMjAwLAogICAgICAgICAgICBpdGVyLm1heD0zMCkKYGBgCgpgYGB7cn0KcGxvdF9jbHVzZ2FwKGdhcHMpCmBgYAoKKiAzIG91IDYgZ3J1cG9zIHBhcmVjZSBhcHJvcGlhZG8sIG1hcyBjb21vIDYgw6kgcHJlY2VkaWRvIHBvciB1bWEgc8OpcmllIGRlIHF1ZWRhcyAzIHNlcmlhIHVtYSBtZWxob3Igb3DDp8Ojby4KCiMjIEVsYm93IE1ldGhvZAoKYGBge3J9CnNldC5zZWVkKDEyMykKIyBDb21wdXRlIGFuZCBwbG90IHdzcyBmb3IgayA9IDIgdG8gayA9IDE1LgprLm1heCA8LSAxNQoKd3NzIDwtIHNhcHBseSgxOmsubWF4LCAKICAgICAgICAgICAgICBmdW5jdGlvbihrKXtrbWVhbnMoc2NhbGVkX2RhdGEsIGssIG5zdGFydD01MCxpdGVyLm1heCA9IDE1ICkkdG90LndpdGhpbnNzfSkKcGxvdCgxOmsubWF4LCB3c3MsCiAgICAgdHlwZT0iYiIsIHBjaCA9IDE5LCBmcmFtZSA9IEZBTFNFLCAKICAgICB4bGFiPSJOdW1iZXIgb2YgY2x1c3RlcnMgSyIsCiAgICAgeWxhYj0iVG90YWwgd2l0aGluLWNsdXN0ZXJzIHN1bSBvZiBzcXVhcmVzIikKYGBgCgoqIFBlbG8gRWxib3cgbWV0aG9kIDMgcGFyZWNlIHNlciB1bSBib20gbsO6bWVybyBkZSBncnVwb3MgZGV2aWRvIMOgIHF1ZWRhIGRlIDMgcGFyYSA0LgoKIyMgQmF5ZXNpYW4gSW5mb3JtYXRpb24gQ3JpdGVyaW9uCgpgYGB7ciBlY2hvPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQpkX2NsdXN0IDwtIE1jbHVzdChhcy5tYXRyaXgoc2NhbGVkX2RhdGEpLCBHPTE6MTUsIAogICAgICAgICAgICAgICAgICBtb2RlbE5hbWVzID0gbWNsdXN0Lm9wdGlvbnMoImVtTW9kZWxOYW1lcyIpKQoKcGxvdChkX2NsdXN0JEJJQykKYGBgCgoqIFZpc3VhbG1lbnRlIEs9IDIgZSBLID0gMyByZXByZXNlbnRhbSBvIGdhbmhvIG1haXMgc2lnbmlmaWNhdGl2byBlbSB0ZXJtb3MgZGUgQklDIChCYXllc2lhbiBJbmZvcm1hdGlvbiBDcml0ZXJpb24pIAoKIyMgSHViZXJ0IEluZGV4IGUgRCBJbmRleAoKYGBge3J9Cm5iIDwtIE5iQ2x1c3Qoc2NhbGVkX2RhdGEsIGRpc3M9TlVMTCwgZGlzdGFuY2UgPSAiZXVjbGlkZWFuIiwgCiAgICAgICAgICAgICAgbWluLm5jPTIsIG1heC5uYz01LCBtZXRob2QgPSAia21lYW5zIiwgCiAgICAgICAgICAgICAgaW5kZXggPSAiYWxsIiwgYWxwaGFCZWFsZSA9IDAuMSkKaGlzdChuYiRCZXN0Lm5jWzEsXSwgYnJlYWtzID0gbWF4KG5hLm9taXQobmIkQmVzdC5uY1sxLF0pKSkKYGBgCgoqIE8gw61uZGljZSBkZSBIdWJlcnQgZSBvIMOtbmRpY2UgRCBzdWdlcmVtIEsgPSAzICBjb21vIGEgbWVsaG9yIHNvbHXDp8OjbwoKIyBLLU1lYW5zIAoKKioqKgoKIyMgQ2x1c3RlcmluZwoKYGBge3J9Cm5fY2x1c3RlcnMgPSAzCgpzY2FsZWRfZGF0YSAlPiUKICAgIGttZWFucyhuX2NsdXN0ZXJzLCBpdGVyLm1heCA9IDEwMCwgbnN0YXJ0ID0gMjApIC0+IGttCgpwIDwtIGF1dG9wbG90KGttLCBkYXRhPXNjYWxlZF9kYXRhLCBmcmFtZSA9IFRSVUUpICAKCmdncGxvdGx5KHApCgpgYGAKCiogw4kgcG9zc8OtdmVsIHZlciBvcyAzIGdydXBvcyBuaXRpZGFtZW50ZSBkaXN0aW50b3MsIHBvciBtZWlvIGRlIHVtIHpvb20gcGVyY2ViZS1zZSBxdWUgZW1ib3JhIG8gZ3J1cG8gMSBlIG8gZ3J1cG8gMyBlc3RlamFtIHByw7N4aW1vcyBvIG92ZXJsYXAgw6kgYmFzaWNhbWVudGUgaW5leGlzdGVudGUuCgpgYGB7ciwgd2FybmluZz1GQUxTRX0Kcm93Lm5hbWVzKHNjYWxlZF9kYXRhKSA8LSBkYXRhJHRpdGxlCgp0b2NsdXN0IDwtIHNjYWxlZF9kYXRhICU+JSAKICAgIHJvd25hbWVzX3RvX2NvbHVtbih2YXIgPSAidGl0bGUiKSAKCmttID0gdG9jbHVzdCAlPiUgCiAgICBzZWxlY3QoLXRpdGxlKSAlPiUgCiAgICBrbWVhbnMoY2VudGVycyA9IG5fY2x1c3RlcnMsIGl0ZXIubWF4ID0gMTAwLCBuc3RhcnQgPSAyMCkKCmttICU+JSAKICAgIGF1Z21lbnQodG9jbHVzdCkgJT4lIAogICAgZ2F0aGVyKGtleSA9ICJ2YXJpw6F2ZWwiLCB2YWx1ZSA9ICJ2YWxvciIsIC10aXRsZSwgLS5jbHVzdGVyKSAlPiUgCiAgICBnZ3Bsb3QoYWVzKHggPSBgdmFyacOhdmVsYCwgeSA9IHZhbG9yLCBncm91cCA9IHRpdGxlLCBjb2xvdXIgPSAuY2x1c3RlcikpICsgCiAgICBnZW9tX3BvaW50KGFscGhhID0gMC4yKSArIAogICAgZ2VvbV9saW5lKGFscGhhID0gLjUpICsgCiAgICBmYWNldF93cmFwKH4gLmNsdXN0ZXIpICsKICAgIGNvb3JkX2ZsaXAoKQoKYGBgCgo8YnI+CgoqIEdydXBvIDEgLSAqKldlIENhbiBEbyBJdCEqKgogICAgKiBNZW5vciBGYXR1cmFtZW50byAKICAgICogTWFpcyBkaWFsw7NnbyBwYXJhIGFzIG11bGhlcmVzIAogICAgKiBNYWlvciB0YXhhIGRlIHBlcnNvbmFnZW5zIGZlbWluaW5vcwoKPGJyPgoKKiBHcnVwbyAyIC0gKipJdCdzIEEgTWFuJ3MgTWFuJ3MgTWFuJ3MgV29ybGQqKiAgIAogICAgKiBNYWlvciBmYXR1cmFtZW50byBlbnRyZSB0b2RvcwogICAgKiBNZW5vciB0YXhhIGRlIGRpYWzDs2dvIHBhcmEgYXMgbXVsaGVyZXMKICAgICogTWVub3IgdGF4YSBkZSBwZXJzb25hZ2VucyBmZW1pbmlub3MKCjxicj4KCiogR3J1cG8gMyAtICoqU2l0dGluZyBvbiB0aGUgRmVuY2UqKgogICAgKiBNZWRpYW5vIGVtIHRlcm1vcyBkZSBkaWFsw7NnbywgcGVyc29uYWdlbnMgZSBmYXR1cmFtZW50byAKCiMjIFNpbGhvdWV0dGUKCmBgYHtyfQpkaXN0cyA9IHNjYWxlZF9kYXRhICU+JSAKICBkaXN0KCkKCnNjYWxlZF9kYXRhICU+JQogICAga21lYW5zKDMsIGl0ZXIubWF4ID0gMTAwLCBuc3RhcnQgPSAyMCkgLT4ga20KCgpzaWxob3VldHRlKGttJGNsdXN0ZXIsIGRpc3RzKSAlPiUKICAgcGxvdChjb2wgPSBSQ29sb3JCcmV3ZXI6OmJyZXdlci5wYWwoNCwgIlNldDIiKSxib3JkZXI9TkEpCmBgYAoKKiBPIHZhbG9yIGRlIDAuNiBkYSBzaWxodWV0YSBzaWduaWZpY2EgcXVlIGEgbm9zc2EgY2x1c3Rlcml6YcOnw6NvIGZvaSByYXpvw6F2ZWwuIOODvijijJDilqBf4pagKeODjuKZqgoK